package core

import (
	"context"
	"fmt"
	"github.com/energye/systray"
	"github.com/wailsapp/wails/v2/pkg/runtime"
	"go.uber.org/zap/zapcore"
	"golang.design/x/hotkey"
	"net"
	"net/http"
	"os"
	"skapp/pkg/config"
	st "skapp/pkg/systray"

	"skapp/pkg/config/toml"
	"skapp/pkg/global"
	"skapp/pkg/logger"
	"skapp/pkg/pid"
	server "skapp/pkg/server"
	"time"

	hk "skapp/pkg/hotkey"
)

var pidFile = "./.pid"

var hotKeys []*hotkey.Hotkey

// App struct
type App struct {
	ctx     context.Context
	srv     *http.Server
	Handler http.Handler
	pidLock *pid.PidLock
}

// NewApp creates a new App application struct
func NewApp() *App {
	app := &App{}
	app.load()
	return app
}
func (a *App) Ctx() context.Context {
	return a.ctx
}

func (a *App) ReloadApp() {
	runtime.WindowReloadApp(a.ctx)
}

func (a *App) ForceClose() {
	a.Shutdown(a.ctx)
	os.Exit(0)
}

func (a *App) load() {
	a.pidLock = pid.NewPidLock(pidFile)

	conf, err := toml.LoadConfig()
	if err != nil {
		logger.Log.Fatalf("%s", err)
	}

	global.Config = conf

	if conf.HasDebug() && conf.Debug.Enable {
		logger.SetLevel(zapcore.DebugLevel)
	} else {
		logger.SetLevel(zapcore.ErrorLevel)
	}

	engine := server.Server(conf)

	a.srv = &http.Server{
		Addr:    fmt.Sprintf("%s:%d", conf.Server.Host, conf.Server.Port),
		Handler: engine,
	}

	a.Handler = engine.Handler()
}

func (a *App) Startup(ctx context.Context) {
	if !global.Config.HasDebug() || !global.Config.Debug.Enable {
		err := a.pidLock.Lock()
		if err != nil {
			os.Exit(-1)
		}
	}

	a.ctx = ctx

	st.Init(func() {
		mainMenuItem := systray.AddMenuItem("主页面", "显示主页面")
		mainMenuItem.Click(func() {
			runtime.WindowShow(ctx)
		})
		systray.AddSeparator()
		quitMenuItem := systray.AddMenuItem("退出", "退出程序")
		quitMenuItem.Click(func() {
			a.ForceClose()
		})
	})

	hotKeys = append(hotKeys, hk.Register([]hotkey.Modifier{hotkey.ModShift, hotkey.ModCtrl, hotkey.ModAlt}, hotkey.KeyX, func() {
		logger.Log.Info("热键触发: Ctrl+Shift+Alt+X")
	}, nil))

	go func() {
		logger.Log.Infof("启动本地后台服务: %s", a.srv.Addr)
		// 服务连接
		if err := a.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			switch err.(type) {
			case *net.OpError:
				_, _ = runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
					Title:   "错误",
					Type:    runtime.ErrorDialog,
					Message: fmt.Sprintf("服务启动失败: 请检查 %s 是否被其他进程占用", a.srv.Addr),
				})
			default:
				_, _ = runtime.MessageDialog(ctx, runtime.MessageDialogOptions{
					Title:   "错误",
					Type:    runtime.ErrorDialog,
					Message: err.Error(),
				})
			}

			logger.Log.Fatalf("listen: %s\n", err)
			os.Exit(-1)
		}
	}()
}

// DomReady is called after the front-end dom has been loaded
// domReady 在前端Dom加载完毕后调用
func (a *App) DomReady(ctx context.Context) {
	// Add your action here
	// 在这里添加你的操作
}

func (a *App) BeforeClose(ctx context.Context) bool {
	for _, k := range hotKeys {
		k.Unregister()
	}

	return false
}

func (a *App) Shutdown(ctx context.Context) {
	ctx2, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if !global.Config.HasDebug() || !global.Config.Debug.Enable {
		a.pidLock.UnLock()
	}

	if err := a.srv.Shutdown(ctx2); err != nil {
		logger.Log.Fatalf("Server Shutdown:", err)
	}
}

func (a *App) GetConfig() *config.Config {
	return global.Config
}

func (a *App) OpenFileDialog(options runtime.OpenDialogOptions) (string, error) {
	return runtime.OpenFileDialog(a.ctx, options)
}

func (a *App) SaveFileDialog(options runtime.SaveDialogOptions) (string, error) {
	return runtime.SaveFileDialog(a.ctx, options)
}
